/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "local-include/reg.h"
#include <cpu/cpu.h>
#include <cpu/ifetch.h>
#include <cpu/decode.h>

#define R(i) gpr(i)
#define Mr vaddr_read
#define Mw vaddr_write

enum {
  TYPE_R, TYPE_I, TYPE_S, TYPE_B, TYPE_U, TYPE_J, 
  TYPE_N, // none
};

#define src1R() do { *src1 = R(rs1); } while (0)
#define src2R() do { *src2 = R(rs2); } while (0)
#define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0)
#define immS() do { *imm = (SEXT(BITS(i, 31, 25), 7) << 5) | BITS(i, 11, 7); } while(0)
#define immB() do { *imm = (SEXT(BITS(i,31,31),1)<<12)|BITS(i,7,7)<<11|BITS(i,30,25)<<5|BITS(i,11,8)<<1;} while(0)
#define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0)
#define immJ() do { *imm = (SEXT(BITS(i, 31, 31), 1)<<20)|BITS(i,19,12)<<12|BITS(i,20,20)<<11|BITS(i,30,21)<<1; } while(0)

static void decode_operand(Decode *s, int *dest, word_t *src1, word_t *src2, word_t *imm, int type) {
  uint32_t i = s->isa.inst.val;
  int rd  = BITS(i, 11, 7);
  int rs1 = BITS(i, 19, 15);
  int rs2 = BITS(i, 24, 20);
  *dest = rd;
  switch (type) {
    case TYPE_R: src1R(); src2R();         break;
    case TYPE_I: src1R();          immI(); break;//ori
    case TYPE_S: src1R(); src2R(); immS(); break;//ori
    case TYPE_B: src1R(); src2R(); immB(); break;
    case TYPE_U:                   immU(); break;//ori
    case TYPE_J:                   immJ(); break; 
  }
  //printf("rd=%s,rs1=%s,rs2=%s,imm=0x%lx %ld\n",reg_name(rd,0),reg_name(rs1,0),reg_name(rs2,0),*imm,(int64_t)*imm);
}

static int decode_exec(Decode *s) {
  int dest = 0;
  word_t src1 = 0, src2 = 0, imm = 0;
  s->dnpc = s->snpc;

#define INSTPAT_INST(s) ((s)->isa.inst.val)
#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \
  decode_operand(s, &dest, &src1, &src2, &imm, concat(TYPE_, type)); \
  __VA_ARGS__ ; \
}

  INSTPAT_START();
  INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui         , U, R(dest) = imm);
  INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc       , U, R(dest) = s->pc + imm);//ori
  INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal_j       , J, R(dest)=s->pc+4,s->dnpc=s->pc+imm);
  INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr_ret    , I, R(dest)=s->pc+4,s->dnpc=(src1+imm)&(~1));
  INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq_beqz    , B, if((sword_t)src1==(sword_t)src2)s->dnpc=s->pc+imm);
  INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne_bnez    , B, if((sword_t)src1!=(sword_t)src2)s->dnpc=s->pc+imm);
  INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt         , B, if((sword_t)src1<(sword_t)src2)s->dnpc=s->pc+imm);
  INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge         , B, if((sword_t)src1>=(sword_t)src2)s->dnpc=s->pc+imm);
  INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu        , B, if(src1<src2)s->dnpc=s->pc+imm);
  INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu        , B, if(src1>=src2)s->dnpc=s->pc+imm);
  INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb          , I, R(dest) = SEXT(BITS(Mr(src1 + imm, 1),7,0),8));
  INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh          , I, R(dest) = SEXT(BITS(Mr(src1 + imm, 2),15,0),16));
  INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw          , I, R(dest) = SEXT(BITS(Mr(src1 + imm, 4),31,0),32));
  INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu         , I, R(dest) = Mr(src1+imm,1));
  INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu         , I, R(dest) = Mr(src1+imm,2));
  INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb          , S, Mw(src1 + imm, 1, BITS(src2,7,0)));
  INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh          , S, Mw(src1 + imm, 2, BITS(src2,16,0)));
  INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw          , S, Mw(src1 + imm, 4, BITS(src2,31,0)));
  INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi_li_mv  , I, R(dest) = src1 + imm);
  INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti        , I, R(dest) = (sword_t)src1<(sword_t)imm?1:0);
  INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu_seqz  , I, R(dest) = src1<imm?1:0);
  INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori        , I, R(dest) = src1^imm);
  INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori         , I, R(dest) = src1|imm);
  INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi        , I, R(dest) = src1&imm);
  INSTPAT("000000? ????? ????? 001 ????? 00100 11", slli        , I, R(dest) = src1<<BITS(imm,5,0));
  INSTPAT("000000? ????? ????? 101 ????? 00100 11", srli        , I, R(dest) = src1>>BITS(imm,5,0));
  INSTPAT("010000? ????? ????? 101 ????? 00100 11", srai        , I, R(dest) = (sword_t)src1>>BITS(imm,5,0));
  INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add         , R, R(dest) = src1+src2);
  INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub         , R, R(dest) = src1-src2);
  INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll         , R, R(dest) = src1<<src2);
  INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt         , R, R(dest) = (sword_t)src1<(sword_t)src2?1:0);
  INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu_snez   , R, R(dest) = src1<src2?1:0);
  INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor         , R, R(dest) = src1^src2);
  INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl         , R, R(dest) = src1>>src2);
  INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra         , R, R(dest) = (sword_t)src1>>src2);
  INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or          , R, R(dest) = src1|src2);
  INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and         , R, R(dest) = src1&src2);

  INSTPAT("??????? ????? ????? 110 ????? 00000 11", lwu         , I, R(dest) = Mr(src1+imm,4));
  INSTPAT("??????? ????? ????? 011 ????? 00000 11", ld          , I, R(dest) = Mr(src1 + imm, 8));//ori
  INSTPAT("??????? ????? ????? 011 ????? 01000 11", sd          , S, Mw(src1 + imm, 8, src2));//ori
  INSTPAT("??????? ????? ????? 000 ????? 00110 11", addiw       , I, R(dest) = SEXT(BITS(src1 + imm,31,0),32));
  INSTPAT("0000000 ????? ????? 001 ????? 00110 11", slliw       , I, R(dest) = SEXT(BITS(src1,31,0)<<BITS(imm,4,0),32));
  INSTPAT("0000000 ????? ????? 101 ????? 00110 11", srliw       , I, R(dest) = SEXT(BITS(src1,31,0)>>BITS(imm,4,0),32));
  INSTPAT("0100000 ????? ????? 101 ????? 00110 11", sraiw       , I, R(dest) = SEXT((int32_t)BITS(src1,31,0)>>BITS(imm,4,0),32));
  INSTPAT("0000000 ????? ????? 000 ????? 01110 11", addw        , R, R(dest) = SEXT(BITS(src1,31,0)+BITS(src2,31,0),32));
  INSTPAT("0100000 ????? ????? 000 ????? 01110 11", subw        , R, R(dest) = SEXT(BITS(src1,31,0)-BITS(src2,31,0),32));
  INSTPAT("0000000 ????? ????? 001 ????? 01110 11", sllw        , R, R(dest) = SEXT(BITS(src1,31,0)<<BITS(src2,4,0),32));
  INSTPAT("0000000 ????? ????? 101 ????? 01110 11", srlw        , R, R(dest) = SEXT(BITS(src1,31,0)>>BITS(src2,4,0),32));
  INSTPAT("0100000 ????? ????? 101 ????? 01110 11", sraw        , R, R(dest) = SEXT((int32_t)BITS(src1,31,0)>>BITS(src2,4,0),32));

  INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw       , I, R(dest) = csrR(imm),csrW(imm,src1));
	INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs       , I, R(dest) = csrR(imm),csrW(imm,csrR(imm)|src1));
  /*INSTPAT("??????? ????? ????? 011 ????? 11100 11", csrrc       , I, R(dest) = csrR(imm),csrW(imm,csrR(imm)&(~src1)));*/
  /*INSTPAT("??????? ????? ????? 101 ????? 11100 11", csrrwi      , I, R(dest) = csrR(imm),csrW(imm,BITS(s->isa.inst.val,19,15)));*/
  /*INSTPAT("??????? ????? ????? 110 ????? 11100 11", csrrsi      , I, R(dest) = csrR(imm),csrW(imm,csrR(imm)|BITS(s->isa.inst.val,19,15)));*/
  /*INSTPAT("??????? ????? ????? 111 ????? 11100 11", csrrci      , I, R(dest) = csrR(imm),csrW(imm,csrR(imm)&(~BITS(s->isa.inst.val,19,15))));*/

  INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul         , R, R(dest) = src1*src2);
  INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div         , R, R(dest) = (sword_t)src1/(sword_t)src2);
  INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu        , R, R(dest) = src1/src2);
  INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem         , R, R(dest) = (sword_t)src1%(sword_t)src2);
  INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu        , R, R(dest) = src1%src2);

  INSTPAT("0000001 ????? ????? 000 ????? 01110 11", mulw        , R, R(dest) = SEXT(BITS(BITS(src1,31,0)*BITS(src2,31,0),31,0),32));
  INSTPAT("0000001 ????? ????? 100 ????? 01110 11", divw        , R, R(dest) = SEXT(BITS(src2,31,0)==0?(int32_t)-1:(int32_t)BITS(src1,31,0)/(int32_t)BITS(src2,31,0),32));
  INSTPAT("0000001 ????? ????? 101 ????? 01110 11", divuw        , R, R(dest) = SEXT(BITS(src2,31,0)==0?(int32_t)-1:BITS(src1,31,0)/BITS(src2,31,0),32));
  INSTPAT("0000001 ????? ????? 110 ????? 01110 11", remw        , R, R(dest) = SEXT(BITS(src2,31,0)==0?BITS(src1,31,0):(int32_t)BITS(src1,31,0)%(int32_t)BITS(src2,31,0),32));
  INSTPAT("0000001 ????? ????? 111 ????? 01110 11", remuw        , R, R(dest) = SEXT(BITS(src2,31,0)==0?BITS(src1,31,0):BITS(src1,31,0)%BITS(src2,31,0),32));

  INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall  , N,isa_raise_intr(0xb,s->pc),s->dnpc=csrR(csrindex("mtvec")));
  INSTPAT("0011000 00010 00000 000 00000 11100 11", mert   , N, s->dnpc=csrR(csrindex("mepc")));
  INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0
  INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));//ori
  INSTPAT_END();

  R(0) = 0; // reset $zero to 0

  return 0;
}

int isa_exec_once(Decode *s) {
  s->isa.inst.val = inst_fetch(&s->snpc, 4);
  return decode_exec(s);
}
